﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace dnAnalytics.Random
{
    public class ThreadSafeRandomNumberGenerator
    {
        private readonly AbstractRandomNumberGenerator _rng;
        private readonly int _cacheSize = 10000;
        private int _position;
        private IList<double> _cache;
        private ReaderWriterLock _lock = new ReaderWriterLock();
        public ThreadSafeRandomNumberGenerator(AbstractRandomNumberGenerator generator) 
        {
            _rng = generator;
            _cache = new List<double>(_rng.NextDouble(_cacheSize));
        }

        public ThreadSafeRandomNumberGenerator(AbstractRandomNumberGenerator generator, int cacheSize)
        {
            _rng = generator;
            _cacheSize = cacheSize;
            _cache = new List<double>(_rng.NextDouble(_cacheSize));
        }

        /// <summary>
        /// Returns an array of uniformly distributed random doubles in the interval [0.0,1.0].
        /// </summary>
        /// <param name="n">The size of the array.</param>
        /// <returns>
        /// An array of uniformly distributed random doubles in the interval [0.0,1.0].
        /// </returns>
        /// <exception cref="ArgumentException">if n is not greater than 0.</exception>
        public double[] NextDouble(int n)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Returns a random number within a specified range.
        /// </summary>
        /// <param name="minValue">The inclusive lower bound of the random number returned.</param>
        /// <param name="maxValue">The exclusive upper bound of the random number returned. <paramref name="maxValue"/> must be greater than or equal to <paramref name="minValue"/>.</param>
        /// <returns>
        /// A 32-bit signed integer greater than or equal to <paramref name="minValue"/> and less than <paramref name="maxValue"/>; that is, the range of return values includes <paramref name="minValue"/> but not <paramref name="maxValue"/>. If <paramref name="minValue"/> equals <paramref name="maxValue"/>, <paramref name="minValue"/> is returned.
        /// </returns>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="minValue"/> is greater than <paramref name="maxValue"/>. </exception>
        public int Next(int minValue, int maxValue)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Fills the elements of a specified array of bytes with random numbers.
        /// </summary>
        /// <param name="buffer">An array of bytes to contain random numbers.</param>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="buffer"/> is null. </exception>
        public void NextBytes(byte[] buffer)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Returns a nonnegative random number.
        /// </summary>
        /// <returns>
        /// A 32-bit signed integer greater than or equal to zero and less than <see cref="F:System.Int32.MaxValue"/>.
        /// </returns>
        public int Next()
        {
            throw new NotSupportedException();
        }

        public double NextDouble()
        {
            _lock.AcquireReaderLock(0);
            double ret = _cache[Interlocked.Increment(ref _position)];
            _lock.ReleaseReaderLock();
            return ret;

        }
    }
}
